﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Cofe.Core.Entry;
using Cofe.Core.Filter;
using Cofe.Core.Implements;
using Cofe.Core.Utils;
using Cofe.Web;

namespace Cofe.Core
{
    [Export(typeof(ICofeService))]
    [ServicePriority(ServicePriorityAttribute.DefaultPriority_COFE)]
    public class EntryXmlSerializer : IEntryXmlSerializer
    {
        #region Constructor

        #endregion

        #region Methods

        protected string valueToString(object value)
        {
            if (value == null)
                return "";
            if (value is Guid)
                return ((Guid)value).ToString("n");
            else return value.ToString();
        }

        protected virtual object getValue(CofeSystemInfo entry, object prop)
        {
            return entry.Properties.Behaviors.GetProperty(prop).Value;
        }

        protected virtual string getValueAsString(CofeSystemInfo entry, object prop)
        {
            return valueToString(getValue(entry, prop));
        }

        protected class propInfo
        {
            public List<KeyValuePair<object, WebMetadataAttribute>> attribeMetadataProps { get; set; }
            public List<KeyValuePair<object, WebMetadataAttribute>> elementMetadataProps { get; set; }
            public IEnumerable<KeyValuePair<object, WebMetadataAttribute>> metadataProps
            {
                get { return attribeMetadataProps.Union(elementMetadataProps); }
            }
        }

        protected propInfo getProps<T>(T entry, bool includeDetails) where T : CofeSystemInfo
        {
            var props = entry.Properties.Behaviors.GetSupportedProperties();
            var metadataProps = props.Where(p => DecoratorExtension.IsMetadata(p))
                .ToDictionary(p => p, p => AttributeUtils<WebMetadataAttribute>.FindAttribute(p));

            return new propInfo()
            {
                attribeMetadataProps = metadataProps
                    .Where(pp => includeDetails || pp.Value.IsMajorProperty<T>())
                    .Where(pp => pp.Value.SerializeAsAttribute).ToList(),

                elementMetadataProps = metadataProps
                    .Where(pp => includeDetails || pp.Value.IsMajorProperty<T>())
                    .Where(pp => !pp.Value.SerializeAsAttribute).ToList()
            };
        }


        public virtual void WriteXml<T>(T entry, XmlWriter writer, bool includeDetails) where T : CofeSystemInfo
        {
            var props = getProps(entry, includeDetails);
            var pd = CofeServices.PropertyDictionary;

            foreach (var ppair in props.attribeMetadataProps)
                writer.WriteAttributeString(pd.GetPropertyString(ppair.Key), getValueAsString(entry, ppair.Key));

            foreach (var ppair in props.elementMetadataProps)
                writer.WriteElementString(pd.GetPropertyString(ppair.Key), getValueAsString(entry, ppair.Key));


        }

        public virtual void WriteXml(IEnumerable<CofeSystemInfo> subEntries, XmlWriter writer, bool IncludeDetails)
        {
            if (subEntries.Any())
            {
                writer.WriteStartElement(XNames.Entries.LocalName, XNames.Entries.NamespaceName);

                foreach (var e in subEntries)
                {
                    writer.WriteStartElement(XNames.Entry.LocalName, XNames.Entries.NamespaceName);
                    writer.WriteAttributeString(XNames.Type.LocalName, e.Type);
                    WriteXml(e as CofeSystemInfo, writer, IncludeDetails);
                    writer.WriteEndElement();
                    //writer.WriteValue(e as CofeSystemInfo);
                }

                writer.WriteEndElement();
            }
        }

        //public virtual void WriteXml<T>(T directory, XmlWriter writer, FilterCollections filters, bool IncludeDetails) where T : CofeDirectoryInfo
        //{
        //    WriteXml(directory, writer, IncludeDetails);
        //    var subEntries = directory.EnumerateCofeSystemInfos(filters, EntryConstructionFlags.Default, CofeServices.EntryConstructor);
        //    if (subEntries.Any())
        //    {
        //        writer.WriteStartElement(XNames.Entries.LocalName, XNames.Entries.NamespaceName);

        //        foreach (var e in subEntries)
        //        {
        //            writer.WriteStartElement(XNames.Entry.LocalName, XNames.Entries.NamespaceName);
        //            WriteXml((e as EntryLink).Entry as CofeSystemInfo, writer, IncludeDetails);
        //            writer.WriteEndElement();
        //            //writer.WriteValue(e as CofeSystemInfo);
        //        }

        //        writer.WriteEndElement();
        //    }
        //}

        public void WriteParameters(ParameterDic pd, SerializationInfo info, StreamingContext context, Func<string, string> parameterFilter = null)
        {
            if (pd != null)
            {
                if (parameterFilter == null)
                    parameterFilter = (key) => key.StartsWith("out_") ? key.Replace("out_", "") : null;
                foreach (var ppair in pd)
                    if (parameterFilter(ppair.Key) != null)
                        if (ppair.Value.GetType().IsPrimitive)
                            info.AddValue(camelCase(parameterFilter(ppair.Key)), ppair.Value);
                        else info.AddValue(camelCase(parameterFilter(ppair.Key)), ppair.Value.ToString());
            }
        }

        public string camelCase(string str)
        {
            return str.Substring(0, 1).ToLower() + str.Substring(1);
        }

        public virtual void WriteObjectData<T>(T entry, SerializationInfo info, StreamingContext context, bool includeDetails = false) where T : CofeSystemInfo
        {
            var props = getProps(entry, includeDetails);
            var pd = CofeServices.PropertyDictionary;

            Guid entryId = entry.Properties.Behaviors.GetProperty<Guid>(CacheProperties.Id);
            
            foreach (var ppair in props.metadataProps)
                info.AddValue(camelCase(pd.GetPropertyString(ppair.Key)), getValue(entry, ppair.Key));
        }

        public virtual void WriteObjectData(IEnumerable<CofeSystemInfo> subEntries, ParameterDic pd, SerializationInfo info, StreamingContext context, bool IncludeDetails = false)
        {
            if (pd != null)
                WriteParameters(pd, info, context);

            info.AddValue("entries", subEntries.ToList());
        }


        public bool ReadObjectData<T>(SerializationInfo info, StreamingContext context, out T entry) where T : CofeSystemInfo
        {
            entry = null;
            return true;
        }

        public bool ReadObjectData(SerializationInfo info, StreamingContext context, out IEnumerable<CofeSystemInfo> subEntries, out ParameterDic pd)
        {
            subEntries = new List<CofeSystemInfo>();
            pd = new ParameterDic();
            return true;
        }

        #endregion

        #region Data

        #endregion

        #region Public Properties

        #endregion







    }
}
